package com.gw.vip.server.service;

import com.gw.vip.common.enums.StatusEnums;
import com.gw.vip.common.exception.BusinessException;
import com.gw.vip.common.exception.ExceptionEnums;
import com.gw.vip.common.exception.ServiceException;
import com.gw.vip.server.enums.OrderTypeEnums;
import com.gw.vip.server.dto.*;
import com.gw.vip.server.po.Account;
import com.gw.vip.server.po.Trans;
import com.gw.vip.server.service.dao_service.TransAllService;
import com.gw.vip.server.service.dao_service.AccountService;
import com.gw.vip.server.service.dao_service.TransService;
import com.gw.vip.server.service.tool_service.AccountTransToolService;
import com.gw.vip.server.vo.AccQueryRes;
import com.gw.vip.server.vo.AccountResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * @author GW 光武
 * @description 帐户核心交易
 * @since 2023-03-12
 */
@Service
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class AccCoreService {

    private final TransAllService transAllService;
    private final AccountTransToolService accountTransToolService;
    private final AccountService accountService;
    private final TransService transService;

    /**
     * 帐户开户
     * @param req
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public AccountResponse accOpen(AccOpenReq req) {
        Trans trans = transAllService.getByBase(req.getClientTransId());
        if (trans!=null){
            log.error("客户流水【{}】已经存在", req.getClientTransId());
            throw new BusinessException("客户流水已经存在！");
        }

        //生成新帐户
        Account account = accountTransToolService.initAccount(req);
        accountService.save(account);

        //记录流水
        trans = accountTransToolService.initTrans(req, account);
        transService.save(trans);

        //更新帐户金额
        accountService.updateAmt(account.getId(), trans.getTxnAmt(), req.getTxnDateTime());

        //重新查询帐户
        account = accountService.getById(account.getId());

        //更新交易
        trans.setBalAmt(account.getAmt());;
        trans.setStatus(StatusEnums.SUCCESS.getCode());
        transService.updateById(trans);

        AccountResponse res = new AccountResponse();
        BeanUtils.copyProperties(trans, res);
        return res;
    }

    /**
     * 帐户充值
     * @param req
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public AccountResponse accCharge(AccChargeReq req) {
        Trans trans = transAllService.getByBase(req.getClientTransId());
        if (trans!=null){
            log.error("客户流水【{}】已经存在", req.getClientTransId());
            throw new BusinessException("客户流水已经存在！");
        }

        Account account = accountService.getById(req.getAccount());
        if (account==null){
            log.error("帐户【{}】不存在！", req.getAccount());
            throw new ServiceException("帐户不存在！");
        }

        trans = accountTransToolService.initTrans(req, account);
        transService.save(trans);

        //更新帐户金额
        accountService.updateAmt(account.getId(), trans.getTxnAmt(), req.getTxnDateTime());

        //重新查询帐户
        account = accountService.getById(account.getId());

        //更新交易
        trans.setBalAmt(account.getAmt());;
        trans.setStatus(StatusEnums.SUCCESS.getCode());
        transService.updateById(trans);

        AccountResponse res = new AccountResponse();
        BeanUtils.copyProperties(trans, res);
        return res;
    }

    /**
     * 帐户消费
     * @param req
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public AccountResponse accSale(AccSaleReq req) {
        Trans trans = transAllService.getByBase(req.getClientTransId());
        if (trans!=null){
            log.error("客户流水【{}】已经存在", req.getClientTransId());
            throw new BusinessException("客户流水已经存在！");
        }

        Account account = accountService.getById(req.getAccount());
        if (account==null){
            log.error("帐户【{}】不存在！", req.getAccount());
            throw new ServiceException("帐户不存在！");
        }

        //可用余额不足
        if (account.getAmt().subtract(account.getLockAmt()).compareTo(req.getTxnAmt())<0){
            log.error("帐户【{}】余额不足。交易金额【{}】，可用余额【{}】", account.getId(), req.getTxnAmt(), account.getAmt().subtract(account.getLockAmt()));
            throw new BusinessException(ExceptionEnums.LOW_AMT, "帐户余额不足！");
        }

        //记录订单
        trans = accountTransToolService.initTrans(req, account);
        transService.save(trans);

        //更新帐户金额
        accountService.updateAmt(account.getId(), trans.getTxnAmt().negate(), req.getTxnDateTime());

        //重新查询帐户
        account = accountService.getById(account.getId());

        //更新交易
        trans.setBalAmt(account.getAmt());;
        trans.setStatus(StatusEnums.SUCCESS.getCode());
        transService.updateById(trans);

        AccountResponse res = new AccountResponse();
        BeanUtils.copyProperties(trans, res);
        return res;
    }

    /**
     * 帐户退款
     * @param req
     * @return
     */
    public AccountResponse accRefund(AccRefundReq req) {
        Trans trans = transAllService.getByBase(req.getClientTransId());
        if (trans!=null){
            log.error("客户流水【{}】已经存在", req.getClientTransId());
            throw new BusinessException(ExceptionEnums.ITEM_IS_EXIST, "客户流水已经存在！");
        }

        Trans srcTrans = transAllService.getByBase(req.getSrcClientTransId());
        if (srcTrans==null){
            log.error("原订单不存在！原客户订单号【{}】", req.getSrcClientTransId());
            throw new BusinessException(ExceptionEnums.ITEM_NOT_EXIST, "原交易不存在！");
        }

        //可退款订单
        List<String> orderTypes = new ArrayList<>();
        orderTypes.add(OrderTypeEnums.SALE.getKey());
        orderTypes.add(OrderTypeEnums.AUTH_SALE.getKey());
        if (!orderTypes.contains(srcTrans.getOrderType())){
            log.error("原交易【{}】不可退款!", srcTrans.getId());
            throw new ServiceException(ExceptionEnums.BUSINESS_FORBIDDEN, "原交易不可退款！");
        }

        //检查可退款金额
        if (srcTrans.getTxnAmt().subtract(srcTrans.getRefundAmt()).compareTo(req.getTxnAmt())<0){
            log.error("原交易【{}】超出可退金额。原交易金额【{}】，已退金额【{}】，此次退款金额【{}】", srcTrans.getId(), srcTrans.getTxnAmt(), srcTrans.getRefundAmt(), req.getTxnAmt());
            throw new BusinessException(ExceptionEnums.BUSINESS_FORBIDDEN, "总退款金额不可大于原交易金额！");
        }

        if (!srcTrans.getAccount().equals(req.getAccount())){
            log.error("帐户不匹配！原交易帐户【{}】，交易帐户【{}】", srcTrans.getAccount(), req.getAccount());
            throw new ServiceException(ExceptionEnums.BUSINESS_FORBIDDEN, "帐户不匹配！");
        }

        //获取帐户信息
        Account account = accountService.getById(req.getAccount());
        if (account==null){
            log.error("帐户【{}】不存在！", req.getAccount());
            throw new ServiceException("帐户不存在！");
        }

        //记录订单
        trans = accountTransToolService.initTrans(req, account, srcTrans);
        transService.save(trans);

        //更新帐户金额
        accountService.updateAmt(account.getId(), trans.getTxnAmt(), req.getTxnDateTime());

        //重新查询帐户
        account = accountService.getById(account.getId());

        //更新交易
        trans.setBalAmt(account.getAmt());;
        trans.setStatus(StatusEnums.SUCCESS.getCode());
        transService.updateById(trans);

        //更新原交易
        srcTrans.setRefundAmt(srcTrans.getRefundAmt().add(req.getTxnAmt()));
        srcTrans.setUpdTime(req.getTxnDateTime());
        transService.updateById(srcTrans);

        AccountResponse res = new AccountResponse();
        BeanUtils.copyProperties(trans, res);
        return res;
    }

    /**
     * 帐户预授权
     * @param req
     * @return
     */
    public AccountResponse accAuth(AccAuthReq req) {
        Trans trans = transAllService.getByBase(req.getClientTransId());
        if (trans!=null){
            log.error("客户流水【{}】已经存在", req.getClientTransId());
            throw new BusinessException("客户流水已经存在！");
        }

        Account account = accountService.getById(req.getAccount());
        if (account==null){
            log.error("帐户【{}】不存在！", req.getAccount());
            throw new ServiceException("帐户不存在！");
        }

        //可用余额不足
        if (account.getAmt().subtract(account.getLockAmt()).compareTo(req.getAuthAmt())<0){
            log.error("帐户【{}】余额不足。交易金额【{}】，可用余额【{}】", account.getId(), req.getAuthAmt(), account.getAmt().subtract(account.getLockAmt()));
            throw new BusinessException(ExceptionEnums.LOW_AMT, "帐户余额不足！");
        }

        //记录订单
        trans = accountTransToolService.initTrans(req, account);
        transService.save(trans);

        //更新帐户锁定金额
        accountService.updateLockAmt(account.getId(), trans.getAuthAmt(), req.getTxnDateTime());

        //重新查询帐户
        account = accountService.getById(account.getId());

        //更新交易
        trans.setBalAmt(account.getAmt());
        trans.setStatus(StatusEnums.TRANSACTION.getCode());
        transService.updateById(trans);

        AccountResponse res = new AccountResponse();
        BeanUtils.copyProperties(trans, res);
        return res;
    }

    /**
     * 帐户取消预授权
     * @param req
     * @return
     */
    public AccountResponse accCancelAuth(AccCancelAuthReq req) {
        Trans srcTrans = transAllService.getByBase(req.getClientTransId());
        if (srcTrans==null){
            log.error("订单不存在！客户订单号【{}】", req.getClientTransId());
            throw new BusinessException(ExceptionEnums.ITEM_NOT_EXIST, "交易不存在！");
        }

        if (!srcTrans.getStatus().equals(StatusEnums.TRANSACTION.getCode())){
            log.error("订单【{}】不是授权状态，不可取消！目前状态【{}】", srcTrans.getId(), srcTrans.getStatus());
            throw new BusinessException(ExceptionEnums.ERROR_SETTING, "订单不是授权状态");
        }

        if (!req.getAccount().equals(srcTrans.getAccount())){
            log.error("帐户不符！订单号【{}】,订单帐户【{}】，上送帐户【{}】", srcTrans.getId(), srcTrans.getAccount(), req.getAccount());
            throw new BusinessException(ExceptionEnums.ERROR_PARAMS, "帐户不符！");
        }

        Account account = accountService.getById(req.getAccount());
        if (account==null){
            log.error("帐户【{}】不存在！", req.getAccount());
            throw new ServiceException("帐户不存在！");
        }

        //更新帐户锁定金额
        accountService.updateLockAmt(account.getId(), srcTrans.getAuthAmt().negate(), req.getTxnDateTime());

        //重新查询帐户
        account = accountService.getById(account.getId());

        //更新原交易
        srcTrans.setBalAmt(account.getAmt().subtract(account.getLockAmt()));
        srcTrans.setStatus(StatusEnums.CANCEL.getCode());
        srcTrans.setCompleteTime(req.getTxnDateTime());
        srcTrans.setUpdTime(req.getTxnDateTime());
        transService.updateById(srcTrans);

        AccountResponse res = new AccountResponse();
        BeanUtils.copyProperties(srcTrans, res);
        return res;
    }

    /**
     * 帐户完成预授权
     * @param req
     * @return
     */
    public AccountResponse accCompleteAuth(AccCompleteAuthReq req) {
        Trans srcTrans = transAllService.getByBase(req.getClientTransId());
        if (srcTrans==null){
            log.error("订单不存在！客户订单号【{}】", req.getClientTransId());
            throw new BusinessException(ExceptionEnums.ITEM_NOT_EXIST, "交易不存在！");
        }

        if (!srcTrans.getStatus().equals(StatusEnums.TRANSACTION.getCode())){
            log.error("订单【{}】不是授权状态，不可完成！目前状态【{}】", srcTrans.getId(), srcTrans.getStatus());
            throw new BusinessException(ExceptionEnums.ERROR_SETTING, "订单不是授权状态");
        }

        if (!req.getAccount().equals(srcTrans.getAccount())){
            log.error("帐户不符！订单号【{}】,订单帐户【{}】，上送帐户【{}】", srcTrans.getId(), srcTrans.getAccount(), req.getAccount());
            throw new BusinessException(ExceptionEnums.ERROR_PARAMS, "帐户不符！");
        }

        if (srcTrans.getAuthAmt().compareTo(req.getTxnAmt())<0){
            log.error("超过可完成金额。订单号【{}】,预授权金额【{}】，完成金额【{}】", srcTrans.getId(), srcTrans.getAuthAmt(), req.getTxnAmt());
            throw new BusinessException(ExceptionEnums.ERROR_PARAMS, "超过可完成金额");
        }

        Account account = accountService.getById(req.getAccount());
        if (account==null){
            log.error("帐户【{}】不存在！", req.getAccount());
            throw new ServiceException("帐户不存在！");
        }

        //更新帐户锁定金额
        accountService.updateLockAmt(account.getId(), srcTrans.getAuthAmt().negate(), req.getTxnDateTime());

        //更新帐户金额
        srcTrans.setTxnAmt(req.getTxnAmt());
        accountService.updateAmt(account.getId(), srcTrans.getTxnAmt().negate(), req.getTxnDateTime());

        //重新查询帐户
        account = accountService.getById(account.getId());

        //更新原交易
        srcTrans.setBalAmt(account.getAmt().subtract(account.getLockAmt()));
        srcTrans.setStatus(StatusEnums.SUCCESS.getCode());
        srcTrans.setCompleteTime(req.getTxnDateTime());
        srcTrans.setUpdTime(req.getTxnDateTime());
        transService.updateById(srcTrans);

        AccountResponse res = new AccountResponse();
        BeanUtils.copyProperties(srcTrans, res);
        return res;
    }

    /**
     * 帐户交易查询
     * @param req
     * @return
     */
    public AccountResponse accTransQuery(AccTransQueryReq req) {
        Trans trans = transAllService.getByBase(req.getClientTransId());
        if (trans==null){
            log.error("订单不存在！客户订单号【{}】", req.getClientTransId());
            throw new BusinessException(ExceptionEnums.ITEM_NOT_EXIST, "订单不存在！");
        }

        AccountResponse res = new AccountResponse();
        BeanUtils.copyProperties(trans, res);
        return res;
    }

    /**
     * 帐户查询
     * @param req
     * @return
     */
    public AccQueryRes accQuery(AccQueryReq req) {
        Account account = accountService.getById(req.getAccount());
        if (account==null){
            log.error("帐户【{}】不存在！", req.getAccount());
            throw new ServiceException(ExceptionEnums.ITEM_NOT_EXIST, "帐户不存在！");
        }

        AccQueryRes res = new AccQueryRes();
        BeanUtils.copyProperties(account, res);
        return res;
    }
}
